package patternmining.model.construction;

import patternmining.model.construction.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;


/** Manages the mapping of short names of types to its fully qualified name, within the scope of a java source file.
 *  Recieves the names of the imported packages or single imported types to establish the mapping valid in the source file.
 *  Mapping includes the types defiened in the owen  of the type defined in the source file and the members of the
 *  impicitly importet java.lang package.
 * 
 *  Mandatory Usage:
 *  1. create one individual instance per source file
 *  2. add arbitrary imports as defined in the source file
 *  3. execute the complete() method, to prepare the instance for queries
 *  4. execute arbitrary queries
 * 
 *  Attention: The implementation requires the call of the complete() method before correct execution of the query methods.
 *  imports added after the complete() method execution will not be effected
 * 
 *  Implements the rule defined for imports im java:
 *  
 * 1) owen type shadows every other type with the same name, imported some way
 * 2) types imported by single import statement ( ex. import java.util.List; ) shadows members of owen package and members of package imports.
 * 3) members of owen package shadow members of package imports.
 * 
 * 4) java.lang.* is imported as package import
 * 
 * 
 *  Example:
 *  
 * import java.util.*;
 * import java.awt.*;
 * 
 * --> simple name "List" will not be known in the file. Every reference to one of the two "List" types has to be completely qualified.
 * 
 * import java.util.*;
 * import java.awt.List;
 * 
 * --> the ocorrence of the simple name "List" in the file will be mapped to java.awt.List. The other type "java.util.List" has to be referenced
 * by its completely qualified name.
 * 
 * 
 *  
 * @version 1.0, 04.07.2008
 * @author Holger Wiehen
 */
class NameSpace {
    
    private PackageIndex packageIndex;
    //types maps short names of types to its fully qualified names  ex String -> java.lang.String
    private Map<String , String > types;
    private Set<String> qualifiedNames;
    private Set<String> mappedSimpleNames;
    private String ownPackage;
    private String ownTypeName;
    private Set<String> singleTypeImports;
    private Set<String> packageImports;
    
    NameSpace(){
        packageIndex = packageIndex = PackageIndex.getInstance(); 
        types = new HashMap<String, String >();
        qualifiedNames = new HashSet<String>();
        mappedSimpleNames = new HashSet<String>();
        singleTypeImports = new HashSet<String>();
        packageImports = new HashSet<String>();
        ownPackage = "default";
    }


    void setOwnPackageName(String packageName){
        ownPackage = packageName;
    }

    void setOwnTypeName(String qualifiedNameOfOwnType){
        ownTypeName = qualifiedNameOfOwnType.substring( qualifiedNameOfOwnType.lastIndexOf(".") + 1 );
    }

    void complete() throws ImportException, UnknownPackageException{
        //1. guarantee mapping of owen type
        //2. guarantee mapping of types imported by single type import
        //3. guarantee mapping of types, members of owen package that are not yet shadowed
        //4. guarantee mapping of types of package import that are not yet shadowed and whose names are unique ( no collission whith types of other package imports )
        String typeName;
        //1.
        mappedSimpleNames.add(ownTypeName);
        types.put(ownTypeName, ownPackage + "." + ownTypeName  );
        //2.
        for( String qualifiedTypeName : singleTypeImports){
            typeName = qualifiedTypeName.substring( qualifiedTypeName.lastIndexOf('.') + 1 );
            if ( mappedSimpleNames.contains( typeName ) )   throw new ImportException();
            mappedSimpleNames.add(typeName);
            types.put(typeName, qualifiedTypeName);
        }
        //3.
        Set<String> packageMembers = packageIndex.getMembersOfPackage(ownPackage);
        for( String qualifiedTypeName : packageMembers ){
            typeName = qualifiedTypeName.substring( qualifiedTypeName.lastIndexOf(".") + 1 );
            if( !mappedSimpleNames.contains(typeName) ){
                mappedSimpleNames.add(typeName);
                types.put(typeName, qualifiedTypeName);
            }
        }
        //4.
        //packageImports.add("java.lang"); remove the comment to import package java.lang implicitly ( needs preparation of class ProjectDependeny.java
        Set<String> singleOcurrence = new HashSet<String>();
        Set<String> doubleOcurrence = new HashSet<String>();
        // first identify those members whose simple names are not unique amoung all packages imported
        for( String  importedPackage : packageImports){
            packageMembers = packageIndex.getMembersOfPackage(importedPackage);
            for( String qualifiedTypeName : packageMembers ){
                typeName = qualifiedTypeName.substring( qualifiedTypeName.lastIndexOf(".") + 1 );
                if( singleOcurrence.contains(typeName) )    doubleOcurrence.add(typeName);
                if( ! singleOcurrence.contains(typeName) )  singleOcurrence.add(typeName);
            }
        }
        // add those members to the mapping whose simple names are unique amoung all packages imported and who are not yet shadowed by imports with higher priority
        for( String  importedPackage : packageImports){
            packageMembers = packageIndex.getMembersOfPackage(importedPackage);
            for( String qualifiedTypeName : packageMembers ){
                typeName = qualifiedTypeName.substring( qualifiedTypeName.lastIndexOf(".") + 1 );
                if( ! doubleOcurrence.contains(typeName) && ! mappedSimpleNames.contains(typeName) ){
                    mappedSimpleNames.add(typeName);
                    types.put(typeName, qualifiedTypeName); 
                }   
            }
        }
        // the mapping of uniquly identifing simple type names to their qualified version is completed     
    }
    
    void addPackageImport(String packageName) throws UnknownPackageException{
        if ( ! packageIndex.existsPackage(packageName) )    throw new UnknownPackageException();
        if( packageName.equals(ownPackage))  return; 
        packageImports.add( packageName );
    }
    
    void addSingleTypeImport(String importValue) throws UnknownTypeException{
        if ( ! packageIndex.existsType(importValue) )       throw new UnknownTypeException();
        if ( importValue.substring(0, importValue.lastIndexOf('.')).equals(ownPackage) )    return;
        singleTypeImports.add(importValue);
    }
   
    String getQualifiedTypeName(String typeName) throws UnknownTypeException{
        if( packageIndex.existsType(typeName) )     return typeName;
        if( ! types.containsKey(typeName)  )    throw new UnknownTypeException();
        return types.get(typeName);
   }
   
}

class ImportException extends Exception{
    
}
